
#include "MetaGraph.h"
#include <math.h>
#include "3dUtil.h"

int CGraphLine::Draw_Line16(int x0,int y0, int x1, int y1, int color, BOOL bClip)
{
	// this function draws a clipped line

	int cxs, cys,
		cxe, cye;

	// clip and draw each line
	cxs = x0;
	cys = y0;
	cxe = x1;
	cye = y1;


	if (bClip == TRUE)
	{
		int nRet = Clip(cxs,cys,cxe,cye);
		if (nRet == 0)
			return -1;
	}


	//start draw
	Draw_Line16_Imp(cxs,cys,cxe,cye, color);

	// return success
	return 0;

} // end Draw_Clip_Line16


///////////////////////////////////////////////////////////

int CGraphLine::Draw_Line(int x0,int y0, int x1, int y1, int color, BOOL bClip)
{
	// this function draws a wireframe triangle

	int cxs, cys,
		cxe, cye;

	// clip and draw each line
	cxs = x0;
	cys = y0;
	cxe = x1;
	cye = y1;

	// clip the line
	if (bClip == TRUE)
	{
		Clip(cxs,cys,cxe,cye);
	}
		
	Draw_Line_Imp(cxs, cys, cxe,cye,color);

	// return success
	return 0;

} // end Draw_Clip_Line

///////////////////////////////////////////////////////////

int CGraphLine::Clip(int &x1,int &y1,int &x2, int &y2)
{
	// this function clips the sent line using the globally defined clipping
	// region

	// internal clipping codes
#define CLIP_CODE_C  0x0000
#define CLIP_CODE_N  0x0008
#define CLIP_CODE_S  0x0004
#define CLIP_CODE_E  0x0002
#define CLIP_CODE_W  0x0001

#define CLIP_CODE_NE 0x000a
#define CLIP_CODE_SE 0x0006
#define CLIP_CODE_NW 0x0009 
#define CLIP_CODE_SW 0x0005

	int xc1=x1, 
		yc1=y1, 
		xc2=x2, 
		yc2=y2;

	int p1_code=0, 
		p2_code=0;

	// determine codes for p1 and p2
	if (y1 < m_clipRect.top)
		p1_code|=CLIP_CODE_N;
	else
		if (y1 > m_clipRect.bottom)
			p1_code|=CLIP_CODE_S;

	if (x1 < m_clipRect.left)
		p1_code|=CLIP_CODE_W;
	else
		if (x1 > m_clipRect.right)
			p1_code|=CLIP_CODE_E;

	if (y2 < m_clipRect.top)
		p2_code|=CLIP_CODE_N;
	else
		if (y2 > m_clipRect.bottom)
			p2_code|=CLIP_CODE_S;

	if (x2 < m_clipRect.left)
		p2_code|=CLIP_CODE_W;
	else
		if (x2 > m_clipRect.right)
			p2_code|=CLIP_CODE_E;

	// try and trivially reject
	if ((p1_code & p2_code)) 
		return(0);

	// test for totally visible, if so leave points untouched
	if (p1_code==0 && p2_code==0)
		return(1);

	// determine end clip point for p1
	switch(p1_code)
	{
	case CLIP_CODE_C: break;

	case CLIP_CODE_N:
		{
			yc1 = m_clipRect.top;
			xc1 = x1 + 0.5+(m_clipRect.top-y1)*(x2-x1)/(y2-y1);
		} break;
	case CLIP_CODE_S:
		{
			yc1 = m_clipRect.bottom;
			xc1 = x1 + 0.5+(m_clipRect.bottom-y1)*(x2-x1)/(y2-y1);
		} break;

	case CLIP_CODE_W:
		{
			xc1 = m_clipRect.left;
			yc1 = y1 + 0.5+(m_clipRect.left-x1)*(y2-y1)/(x2-x1);
		} break;

	case CLIP_CODE_E:
		{
			xc1 = m_clipRect.right;
			yc1 = y1 + 0.5+(m_clipRect.right-x1)*(y2-y1)/(x2-x1);
		} break;

		// these cases are more complex, must compute 2 intersections
	case CLIP_CODE_NE:
		{
			// north hline intersection
			yc1 = m_clipRect.top;
			xc1 = x1 + 0.5+(m_clipRect.top-y1)*(x2-x1)/(y2-y1);

			// test if intersection is valid, of so then done, else compute next
			if (xc1 < m_clipRect.left || xc1 > m_clipRect.right)
			{
				// east vline intersection
				xc1 = m_clipRect.right;
				yc1 = y1 + 0.5+(m_clipRect.right-x1)*(y2-y1)/(x2-x1);
			} // end if

		} break;

	case CLIP_CODE_SE:
		{
			// south hline intersection
			yc1 = m_clipRect.bottom;
			xc1 = x1 + 0.5+(m_clipRect.bottom-y1)*(x2-x1)/(y2-y1);	

			// test if intersection is valid, of so then done, else compute next
			if (xc1 < m_clipRect.left || xc1 > m_clipRect.right)
			{
				// east vline intersection
				xc1 = m_clipRect.right;
				yc1 = y1 + 0.5+(m_clipRect.right-x1)*(y2-y1)/(x2-x1);
			} // end if

		} break;

	case CLIP_CODE_NW: 
		{
			// north hline intersection
			yc1 = m_clipRect.top;
			xc1 = x1 + 0.5+(m_clipRect.top-y1)*(x2-x1)/(y2-y1);

			// test if intersection is valid, of so then done, else compute next
			if (xc1 < m_clipRect.left || xc1 > m_clipRect.right)
			{
				xc1 = m_clipRect.left;
				yc1 = y1 + 0.5+(m_clipRect.left-x1)*(y2-y1)/(x2-x1);	
			} // end if

		} break;

	case CLIP_CODE_SW:
		{
			// south hline intersection
			yc1 = m_clipRect.bottom;
			xc1 = x1 + 0.5+(m_clipRect.bottom-y1)*(x2-x1)/(y2-y1);	

			// test if intersection is valid, of so then done, else compute next
			if (xc1 < m_clipRect.left || xc1 > m_clipRect.right)
			{
				xc1 = m_clipRect.left;
				yc1 = y1 + 0.5+(m_clipRect.left-x1)*(y2-y1)/(x2-x1);	
			} // end if

		} break;

	default:break;

	} // end switch

	// determine clip point for p2
	switch(p2_code)
	{
	case CLIP_CODE_C: break;

	case CLIP_CODE_N:
		{
			yc2 = m_clipRect.top;
			xc2 = x2 + (m_clipRect.top-y2)*(x1-x2)/(y1-y2);
		} break;

	case CLIP_CODE_S:
		{
			yc2 = m_clipRect.bottom;
			xc2 = x2 + (m_clipRect.bottom-y2)*(x1-x2)/(y1-y2);
		} break;

	case CLIP_CODE_W:
		{
			xc2 = m_clipRect.left;
			yc2 = y2 + (m_clipRect.left-x2)*(y1-y2)/(x1-x2);
		} break;

	case CLIP_CODE_E:
		{
			xc2 = m_clipRect.right;
			yc2 = y2 + (m_clipRect.right-x2)*(y1-y2)/(x1-x2);
		} break;

		// these cases are more complex, must compute 2 intersections
	case CLIP_CODE_NE:
		{
			// north hline intersection
			yc2 = m_clipRect.top;
			xc2 = x2 + 0.5+(m_clipRect.top-y2)*(x1-x2)/(y1-y2);

			// test if intersection is valid, of so then done, else compute next
			if (xc2 < m_clipRect.left || xc2 > m_clipRect.right)
			{
				// east vline intersection
				xc2 = m_clipRect.right;
				yc2 = y2 + 0.5+(m_clipRect.right-x2)*(y1-y2)/(x1-x2);
			} // end if

		} break;

	case CLIP_CODE_SE:
		{
			// south hline intersection
			yc2 = m_clipRect.bottom;
			xc2 = x2 + 0.5+(m_clipRect.bottom-y2)*(x1-x2)/(y1-y2);	

			// test if intersection is valid, of so then done, else compute next
			if (xc2 < m_clipRect.left || xc2 > m_clipRect.right)
			{
				// east vline intersection
				xc2 = m_clipRect.right;
				yc2 = y2 + 0.5+(m_clipRect.right-x2)*(y1-y2)/(x1-x2);
			} // end if

		} break;

	case CLIP_CODE_NW: 
		{
			// north hline intersection
			yc2 = m_clipRect.top;
			xc2 = x2 + 0.5+(m_clipRect.top-y2)*(x1-x2)/(y1-y2);

			// test if intersection is valid, of so then done, else compute next
			if (xc2 < m_clipRect.left || xc2 > m_clipRect.right)
			{
				xc2 = m_clipRect.left;
				yc2 = y2 + 0.5+(m_clipRect.left-x2)*(y1-y2)/(x1-x2);	
			} // end if

		} break;

	case CLIP_CODE_SW:
		{
			// south hline intersection
			yc2 = m_clipRect.bottom;
			xc2 = x2 + 0.5+(m_clipRect.bottom-y2)*(x1-x2)/(y1-y2);	

			// test if intersection is valid, of so then done, else compute next
			if (xc2 < m_clipRect.left || xc2 > m_clipRect.right)
			{
				xc2 = m_clipRect.left;
				yc2 = y2 + 0.5+(m_clipRect.left-x2)*(y1-y2)/(x1-x2);	
			} // end if

		} break;

	default:break;

	} // end switch

	// do bounds check
	if ((xc1 < m_clipRect.left) || (xc1 > m_clipRect.right) ||
		(yc1 < m_clipRect.top) || (yc1 > m_clipRect.bottom) ||
		(xc2 < m_clipRect.left) || (xc2 > m_clipRect.right) ||
		(yc2 < m_clipRect.top) || (yc2 > m_clipRect.bottom) )
	{
		return(0);
	} // end if

	// store vars back
	x1 = xc1;
	y1 = yc1;
	x2 = xc2;
	y2 = yc2;

	return(1);

} // end Clip_Line

///////////////////////////////////////////////////////////

int CGraphLine::Draw_Line_Imp(	int x0, int y0, // starting position 
								int x1, int y1, // ending position
								int color)     // color index
{
	// this function draws a line from xo,yo to x1,y1 using differential error
	// terms (based on Bresenahams work)

	int dx,             // difference in x's
		dy,             // difference in y's
		dx2,            // dx,dy * 2
		dy2, 
		x_inc,          // amount in pixel space to move during drawing
		y_inc,          // amount in pixel space to move during drawing
		error,          // the discriminant i.e. error i.e. decision variable
		index;          // used for looping


	// pre-compute first pixel address in video buffer
	uint8* vb_start  = m_pBuffer + x0 + y0*m_lpitch;

	// compute horizontal and vertical deltas
	dx = x1-x0;
	dy = y1-y0;

	// test which direction the line is going in i.e. slope angle
	if (dx>=0)
	{
		x_inc = 1;

	} // end if line is moving right
	else
	{
		x_inc = -1;
		dx    = -dx;  // need absolute value

	} // end else moving left

	// test y component of slope

	if (dy>=0)
	{
		y_inc = m_lpitch;
	} // end if line is moving down
	else
	{
		y_inc = -m_lpitch;
		dy    = -dy;  // need absolute value

	} // end else moving up

	// compute (dx,dy) * 2
	dx2 = dx << 1;
	dy2 = dy << 1;

	// now based on which delta is greater we can draw the line
	if (dx > dy)
	{
		// initialize error term
		error = dy2 - dx; 

		// draw the line
		for (index=0; index <= dx; index++)
		{
			// set the pixel
			*vb_start = color;

			// test if error has overflowed
			if (error >= 0) 
			{
				error-=dx2;

				// move to next line
				vb_start+=y_inc;

			} // end if error overflowed

			// adjust the error term
			error+=dy2;

			// move to the next pixel
			vb_start+=x_inc;

		} // end for

	} // end if |slope| <= 1
	else
	{
		// initialize error term
		error = dx2 - dy; 

		// draw the line
		for (index=0; index <= dy; index++)
		{
			// set the pixel
			*vb_start = color;

			// test if error overflowed
			if (error >= 0)
			{
				error-=dy2;

				// move to next line
				vb_start+=x_inc;

			} // end if error overflowed

			// adjust the error term
			error+=dx2;

			// move to the next pixel
			vb_start+=y_inc;

		} // end for

	} // end else |slope| > 1

	// return success
	return(1);

} // end Draw_Line

///////////////////////////////////////////////////////////

int CGraphLine::Draw_Line16_Imp(int x0, int y0, // starting position 
								int x1, int y1, // ending position
								int color)     // color index
{
	// this function draws a line from xo,yo to x1,y1 using differential error
	// terms (based on Bresenahams work)

	int dx,             // difference in x's
		dy,             // difference in y's
		dx2,            // dx,dy * 2
		dy2, 
		x_inc,          // amount in pixel space to move during drawing
		y_inc,          // amount in pixel space to move during drawing
		error,          // the discriminant i.e. error i.e. decision variable
		index;          // used for looping

	int lpitch_2 = m_lpitch >> 1; // USHORT strided lpitch

	// pre-compute first pixel address in video buffer based on 16bit data
	USHORT *vb_start2 = (USHORT *)m_pBuffer + x0 + y0*lpitch_2;

	// compute horizontal and vertical deltas
	dx = x1-x0;
	dy = y1-y0;

	// test which direction the line is going in i.e. slope angle
	if (dx>=0)
	{
		x_inc = 1;

	} // end if line is moving right
	else
	{
		x_inc = -1;
		dx    = -dx;  // need absolute value

	} // end else moving left

	// test y component of slope

	if (dy>=0)
	{
		y_inc = lpitch_2;
	} // end if line is moving down
	else
	{
		y_inc = -lpitch_2;
		dy    = -dy;  // need absolute value

	} // end else moving up

	// compute (dx,dy) * 2
	dx2 = dx << 1;
	dy2 = dy << 1;

	// now based on which delta is greater we can draw the line
	if (dx > dy)
	{
		// initialize error term
		error = dy2 - dx; 

		// draw the line
		for (index=0; index <= dx; index++)
		{
			// set the pixel
			*vb_start2 = (USHORT)color;

			// test if error has overflowed
			if (error >= 0) 
			{
				error-=dx2;

				// move to next line
				vb_start2+=y_inc;

			} // end if error overflowed

			// adjust the error term
			error+=dy2;

			// move to the next pixel
			vb_start2+=x_inc;

		} // end for

	} // end if |slope| <= 1
	else
	{
		// initialize error term
		error = dx2 - dy; 

		// draw the line
		for (index=0; index <= dy; index++)
		{
			// set the pixel
			*vb_start2 = (USHORT)color;

			// test if error overflowed
			if (error >= 0)
			{
				error-=dy2;

				// move to next line
				vb_start2+=x_inc;

			} // end if error overflowed

			// adjust the error term
			error+=dx2;

			// move to the next pixel
			vb_start2+=y_inc;

		} // end for

	} // end else |slope| > 1

	// return success
	return 0;

} // end Draw_Line16



//

int CGraphTrig::Draw_Top_Tri(	int x1,int y1, 
								int x2,int y2, 
								int x3,int y3,
								int color)
{
	// this function draws a triangle that has a flat top

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	// destination address of next scanline
	UCHAR  *dest_addr = NULL;

	// test order of x1 and x2
	if (x2 < x1)
	{
		temp_x = x2;
		x2     = x1;
		x1     = temp_x;
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x3-x1)/height;
	dx_right = (x3-x2)/height;

	// set starting points
	xs = (float)x1;
	xe = (float)x2+(float)0.5;

	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(float)(-y1+m_clipRect.top);
		xe = xe+dx_right*(float)(-y1+m_clipRect.top);

		// reset y1
		y1=m_clipRect.top;

	} // end if top is off screen

	if (y3>m_clipRect.bottom)
		y3=m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = m_pBuffer+y1*m_lpitch;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			memset((UCHAR *)dest_addr+(unsigned int)xs, color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = (int)xs;
			right = (int)xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			memset((UCHAR  *)dest_addr+(unsigned int)left, color,(unsigned int)(right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Top_Tri

/////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Bottom_Tri(int x1,int y1, 
								int x2,int y2, 
								int x3,int y3,
								int color)
{
	// this function draws a triangle that has a flat bottom

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	// destination address of next scanline
	UCHAR  *dest_addr;

	// test order of x1 and x2
	if (x3 < x2)
	{
		temp_x = x2;
		x2     = x3;
		x3     = temp_x;
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x2-x1)/height;
	dx_right = (x3-x1)/height;

	// set starting points
	xs = (float)x1;
	xe = (float)x1; // +(float)0.5;

	// perform y clipping
	if (y1<m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(float)(-y1+m_clipRect.top);
		xe = xe+dx_right*(float)(-y1+m_clipRect.top);

		// reset y1
		y1=m_clipRect.top;

	} // end if top is off screen

	if (y3>m_clipRect.bottom)
		y3=m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = m_pBuffer+y1*m_lpitch;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			memset((UCHAR  *)dest_addr+(unsigned int)xs, color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle

		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = (int)xs;
			right = (int)xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			memset((UCHAR  *)dest_addr+(unsigned int)left, color,(unsigned int)(right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Bottom_Tri

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Top_Tri16(	int x1,int y1, 
								int x2,int y2, 
								int x3,int y3,
								int color)
{
	// this function draws a triangle that has a flat top

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	// cast dest buffer to ushort
	USHORT *dest_buffer = (USHORT *)m_pBuffer;

	// destination address of next scanline
	USHORT  *dest_addr = NULL;

	// recompute m_lpitch in 16-bit words
	int lpitch_2 = m_lpitch >> 1;

	// test order of x1 and x2
	if (x2 < x1)
	{
		temp_x = x2;
		x2     = x1;
		x1     = temp_x;
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x3-x1)/height;
	dx_right = (x3-x2)/height;

	// set starting points
	xs = (float)x1;
	xe = (float)x2; // +(float)0.5;

	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(float)(-y1+m_clipRect.top);
		xe = xe+dx_right*(float)(-y1+m_clipRect.top);

		// reset y1
		y1=m_clipRect.top;

	} // end if top is off screen

	if (y3>m_clipRect.bottom)
		y3=m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = dest_buffer+y1*lpitch_2;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=lpitch_2)
		{
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(xs),color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=lpitch_2)
		{
			// do x clip
			left  = (int)xs;
			right = (int)xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(left),color,(unsigned int)(right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Top_Tri16

/////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Bottom_Tri16(	int x1,int y1, 
									int x2,int y2, 
									int x3,int y3,
									int color)
{
	// this function draws a triangle that has a flat bottom

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	// cast dest buffer to ushort
	USHORT *dest_buffer = (USHORT *)m_pBuffer;

	// destination address of next scanline
	USHORT  *dest_addr = NULL;

	// recompute m_lpitch in 16-bit words
	int lpitch_2 = m_lpitch >> 1;

	// test order of x1 and x2
	if (x3 < x2)
	{
		temp_x = x2;
		x2     = x3;
		x3     = temp_x;
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x2-x1)/height;
	dx_right = (x3-x1)/height;

	// set starting points
	xs = (float)x1;
	xe = (float)x1; // +(float)0.5;

	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(float)(-y1+m_clipRect.top);
		xe = xe+dx_right*(float)(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;

	} // end if top is off screen

	if (y3 > m_clipRect.bottom)
		y3 = m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = dest_buffer+y1*lpitch_2;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=lpitch_2)
		{
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(xs),color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=lpitch_2)
		{
			// do x clip
			left  = (int)xs;
			right = (int)xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(left),color,(unsigned int)(right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Bottom_Tri16

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_TrigFP_2D(	int x1,int y1,
								int x2,int y2,
								int x3,int y3,
								int color)
{
	// this function draws a triangle on the destination buffer using fixed point
	// it decomposes all triangles into a pair of flat top, flat bottom

	int temp_x, // used for sorting
		temp_y,
		new_x;

#ifdef DEBUG_ON
	// track rendering stats
	debug_polys_rendered_per_frame++;
#endif


	// test for h lines and v lines
	if ((x1==x2 && x2==x3)  ||  (y1==y2 && y2==y3))
		return 0;

	// sort p1,p2,p3 in ascending y order
	if (y2<y1)
	{
		temp_x = x2;
		temp_y = y2;
		x2     = x1;
		y2     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// now we know that p1 and p2 are in order
	if (y3<y1)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x1;
		y3     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// finally test y3 against y2
	if (y3<y2)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x2;
		y3     = y2;
		x2     = temp_x;
		y2     = temp_y;

	} // end if

	// do trivial rejection tests for clipping
	if ( y3<m_clipRect.top || y1>m_clipRect.bottom ||
		(x1<m_clipRect.left && x2<m_clipRect.left && x3<m_clipRect.left) ||
		(x1>m_clipRect.right && x2>m_clipRect.right && x3>m_clipRect.right) )
		return 0;

	// test if top of triangle is flat
	if (y1==y2)
	{
		Draw_Top_TriFP(x1,y1,x2,y2,x3,y3,color);
	} // end if
	else
		if (y2==y3)
		{
			Draw_Bottom_TriFP(x1,y1,x2,y2,x3,y3,color);
		} // end if bottom is flat
		else
		{
			// general triangle that's needs to be broken up along long edge
			new_x = x1 + (int)(0.5+(float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));

			// draw each sub-triangle
			Draw_Bottom_TriFP(x1,y1,new_x,y2,x2,y2,color);
			Draw_Top_TriFP(x2,y2,new_x,y2,x3,y3,color);

		} // end else

	return 0;
} // end Draw_TrigFP_2D

/////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Trig_2D(	int x1,int y1,
								int x2,int y2,
								int x3,int y3,
								int color)
{
	// this function draws a triangle on the destination buffer
	// it decomposes all triangles into a pair of flat top, flat bottom

	int temp_x, // used for sorting
		temp_y,
		new_x;

#ifdef DEBUG_ON
	// track rendering stats
	debug_polys_rendered_per_frame++;
#endif


	// test for h lines and v lines
	if ((x1==x2 && x2==x3)  ||  (y1==y2 && y2==y3))
		return 0;

	// sort p1,p2,p3 in ascending y order
	if (y2<y1)
	{
		temp_x = x2;
		temp_y = y2;
		x2     = x1;
		y2     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// now we know that p1 and p2 are in order
	if (y3<y1)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x1;
		y3     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// finally test y3 against y2
	if (y3<y2)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x2;
		y3     = y2;
		x2     = temp_x;
		y2     = temp_y;

	} // end if

	// do trivial rejection tests for clipping
	if ( y3<m_clipRect.top || y1>m_clipRect.bottom ||
		(x1<m_clipRect.left && x2<m_clipRect.left && x3<m_clipRect.left) ||
		(x1>m_clipRect.right && x2>m_clipRect.right && x3>m_clipRect.right) )
		return 0;

	// test if top of triangle is flat
	if (y1==y2)
	{
		Draw_Top_Tri(x1,y1,x2,y2,x3,y3,color);
	} // end if
	else
		if (y2==y3)
		{
			Draw_Bottom_Tri(x1,y1,x2,y2,x3,y3,color);
		} // end if bottom is flat
		else
		{
			// general triangle that's needs to be broken up along long edge
			new_x = x1 + (int)(0.5+(float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));

			// draw each sub-triangle
			Draw_Bottom_Tri(x1,y1,new_x,y2,x2,y2,color);
			Draw_Top_Tri(x2,y2,new_x,y2,x3,y3,color);

		} // end else

	return 0;
} // end Draw_Trig_2D

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Trig_2D16(	int x1,int y1,
								int x2,int y2,
								int x3,int y3,
								int color)
{
	// this function draws a triangle on the destination buffer
	// it decomposes all triangles into a pair of flat top, flat bottom

	int temp_x, // used for sorting
		temp_y,
		new_x;

#ifdef DEBUG_ON
	// track rendering stats
	debug_polys_rendered_per_frame++;
#endif


	// test for h lines and v lines
	if ((x1==x2 && x2==x3)  ||  (y1==y2 && y2==y3))
		return 0;

	// sort p1,p2,p3 in ascending y order
	if (y2<y1)
	{
		temp_x = x2;
		temp_y = y2;
		x2     = x1;
		y2     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// now we know that p1 and p2 are in order
	if (y3<y1)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x1;
		y3     = y1;
		x1     = temp_x;
		y1     = temp_y;
	} // end if

	// finally test y3 against y2
	if (y3<y2)
	{
		temp_x = x3;
		temp_y = y3;
		x3     = x2;
		y3     = y2;
		x2     = temp_x;
		y2     = temp_y;

	} // end if

	// do trivial rejection tests for clipping
	if ( y3<m_clipRect.top || y1>m_clipRect.bottom ||
		(x1<m_clipRect.left && x2<m_clipRect.left && x3<m_clipRect.left) ||
		(x1>m_clipRect.right && x2>m_clipRect.right && x3>m_clipRect.right) )
		return 0;

	// test if top of triangle is flat
	if (y1==y2)
	{
		Draw_Top_Tri16(x1,y1,x2,y2,x3,y3,color);
	} // end if
	else
		if (y2==y3)
		{
			Draw_Bottom_Tri16(x1,y1,x2,y2,x3,y3,color);
		} // end if bottom is flat
		else
		{
			// general triangle that's needs to be broken up along long edge
			new_x = x1 + (int)(0.5+(float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));

			// draw each sub-triangle
			Draw_Bottom_Tri16(x1,y1,new_x,y2,x2,y2,color);
			Draw_Top_Tri16(x2,y2,new_x,y2,x3,y3,color);

		} // end else

	return 0;
} // end Draw_Trig_2D16

////////////////////////////////////////////////////////////////////////////////
#if 0
inline void Draw_QuadFP_2D(int x0,int y0,
						   int x1,int y1,
						   int x2,int y2,
						   int x3, int y3,
						   int color,
						   UCHAR *dest_buffer, int m_lpitch)
{
	// this function draws a 2D quadrilateral

	// simply call the triangle function 2x, let it do all the work
	Draw_TriangleFP_2D(x0,y0,x1,y1,x3,y3,color,dest_buffer,m_lpitch);
	Draw_TriangleFP_2D(x1,y1,x2,y2,x3,y3,color,dest_buffer,m_lpitch);

} // end Draw_QuadFP_2D
#endif
////////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Top_TriFP(	int x1,int y1,
								int x2,int y2, 
								int x3,int y3,
								int color)
{
	// this function draws a triangle that has a flat top using fixed point math

	int dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	UCHAR  *dest_addr;

	// test for degenerate
	if (y1==y3 || y2==y3)
		return 0;

	// test order of x1 and x2
	if (x2 < x1)
	{
		temp_x = x2;
		x2     = x1;
		x1     = temp_x;
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = ((x3-x1)<<FIXP16_SHIFT)/height;
	dx_right = ((x3-x2)<<FIXP16_SHIFT)/height;

	// set starting points
	xs = (x1<<FIXP16_SHIFT);
	xe = (x2<<FIXP16_SHIFT);

	// perform y clipping
	if (y1<m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1=m_clipRect.top;

	} // end if top is off screen

	if (y3>m_clipRect.bottom)
		y3=m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = m_pBuffer+y1*m_lpitch;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			memset((UCHAR *)dest_addr+((xs+FIXP16_ROUND_UP)>>FIXP16_SHIFT),
				color, (((xe-xs+FIXP16_ROUND_UP)>>FIXP16_SHIFT)+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = ((xs+FIXP16_ROUND_UP)>>16);
			right = ((xe+FIXP16_ROUND_UP)>>16);

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			memset((UCHAR  *)dest_addr+(unsigned int)left,
				color,(unsigned int)(right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Top_TriFP

/////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Bottom_TriFP(	int x1,int y1, 
									int x2,int y2, 
									int x3,int y3,
									int color)
{

	// this function draws a triangle that has a flat bottom using fixed point math

	int dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height;      // the height of the triangle

	int temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	UCHAR  *dest_addr;

	if (y1==y2 || y1==y3)
		return 0;

	// test order of x1 and x2
	if (x3 < x2)
	{
		temp_x = x2;
		x2     = x3;
		x3     = temp_x;

	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = ((x2-x1)<<FIXP16_SHIFT)/height;
	dx_right = ((x3-x1)<<FIXP16_SHIFT)/height;

	// set starting points
	xs = (x1<<FIXP16_SHIFT);
	xe = (x1<<FIXP16_SHIFT); 

	// perform y clipping
	if (y1<m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1=m_clipRect.top;

	} // end if top is off screen

	if (y3>m_clipRect.bottom)
		y3=m_clipRect.bottom;

	// compute starting address in video memory
	dest_addr = m_pBuffer+y1*m_lpitch;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			memset((UCHAR *)dest_addr+((xs+FIXP16_ROUND_UP)>>FIXP16_SHIFT),
				color, (((xe-xs+FIXP16_ROUND_UP)>>FIXP16_SHIFT)+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = ((xs+FIXP16_ROUND_UP)>>FIXP16_SHIFT);
			right = ((xe+FIXP16_ROUND_UP)>>FIXP16_SHIFT);

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			memset((UCHAR *)dest_addr+left,
				color, (right-left+1));

		} // end for

	} // end else x clipping needed

	return 0;

} // end Draw_Bottom_TriFP

//
//

int CGraphTrig::Draw_Top_Tri2_16(	float x1, float y1, 
									float x2, float y2, 
									float x3, float y3,
									int color)
{
	// this function draws a triangle that has a flat top

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height,      // the height of the triangle
		temp_x,        // used during sorting as temps
		temp_y,
		right,         // used by clipping
		left;

	int iy1,iy3,loop_y; // integers for y looping

	// cast dest buffer to ushort
	USHORT *dest_buffer = (USHORT *)m_pBuffer;

	// destination address of next scanline
	USHORT *dest_addr = NULL;

	// recompute m_lpitch in 16-bit words
	int lpitch_2 = m_lpitch >> 1;

	// test order of x1 and x2
	if (x2 < x1)
	{
		SWAP(x1,x2,temp_x);
	} // end if swap

	// compute delta's
	height = y3 - y1;

	dx_left  = (x3-x1) / height;
	dx_right = (x3-x2) / height;

	// set starting points
	xs = x1;
	xe = x2; 

#if (RASTERIZER_MODE==RASTERIZER_ACCURATE)

	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;

		// make sure top left fill convention is observed
		iy1 = y1;
	} // end if top is off screen
	else
	{
		// make sure top left fill convention is observed
		iy1 = ceil(y1);

		// bump xs and xe appropriately
		xs = xs+dx_left*(iy1-y1);
		xe = xe+dx_right*(iy1-y1);
	} // end else

	if (y3 > m_clipRect.bottom)
	{
		// clip y
		y3 = m_clipRect.bottom;

		// make sure top left fill convention is observed
		iy3 = y3-1;
	} // end if
	else
	{
		// make sure top left fill convention is observed
		iy3 = ceil(y3)-1;
	} // end else
#endif

#if ( (RASTERIZER_MODE==RASTERIZER_FAST) || (RASTERIZER_MODE==RASTERIZER_FASTEST) )
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;
	} // end if top is off screen

	if (y3 > m_clipRect.bottom)
		y3 = m_clipRect.bottom;

	// make sure top left fill convention is observed
	iy1 = ceil(y1);
	iy3 = ceil(y3)-1;
#endif 

	//Write_Error("\nTri-Top: xs=%f, xe=%f, y1=%f, y3=%f, iy1=%d, iy3=%d", xs,xe,y1,y3,iy1,iy3);

	// compute starting address in video memory
	dest_addr = dest_buffer + iy1*lpitch_2;

	// test if x clipping is needed
	if (x1 >= m_clipRect.left && x1 <= m_clipRect.right &&
		x2 >= m_clipRect.left && x2 <= m_clipRect.right &&
		x3 >= m_clipRect.left && x3 <= m_clipRect.right)
	{
		// draw the triangle
		for (loop_y=iy1; loop_y <= iy3; loop_y++, dest_addr+=lpitch_2)
		{
			//Write_Error("\nxs=%f, xe=%f", xs,xe);
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(xs),color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;
		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (loop_y=iy1; loop_y <= iy3; loop_y++, dest_addr+=lpitch_2)
		{
			// do x clip
			left  = xs;
			right = xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}
			//Write_Error("\nleft=%f, right=%f", left,right);
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(left),color,(unsigned int)((int)right-(int)left+1));

		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Top_Tri2_16

/////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Bottom_Tri2_16(float x1, float y1, 
									float x2, float y2, 
									float x3, float y3,
									int color)
{
	// this function draws a triangle that has a flat bottom

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height,      // the height of the triangle
		temp_x,      // used during sorting as temps
		temp_y,
		right,       // used by clipping
		left;

	int iy1,iy3,loop_y;

	// cast dest buffer to ushort
	USHORT *dest_buffer = (USHORT *)m_pBuffer;

	// destination address of next scanline
	USHORT  *dest_addr = NULL;

	// recompute m_lpitch in 16-bit words
	int lpitch_2 = m_lpitch >> 1;

	// test order of x1 and x2
	if (x3 < x2)
	{
		SWAP(x2,x3,temp_x);
	} // end if swap

	// compute delta's
	height = y3 - y1;

	dx_left  = (x2-x1)/height;
	dx_right = (x3-x1)/height;

	// set starting points
	xs = x1;
	xe = x1; 

#if (RASTERIZER_MODE==RASTERIZER_ACCURATE)
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;

		// make sure top left fill convention is observed
		iy1 = y1;
	} // end if top is off screen
	else
	{
		// make sure top left fill convention is observed
		iy1 = ceil(y1);

		// bump xs and xe appropriately
		xs = xs+dx_left*(iy1-y1);
		xe = xe+dx_right*(iy1-y1);
	} // end else

	if (y3 > m_clipRect.bottom)
	{
		// clip y
		y3 = m_clipRect.bottom;

		// make sure top left fill convention is observed
		iy3 = y3-1;
	} // end if
	else
	{
		// make sure top left fill convention is observed
		iy3 = ceil(y3)-1;
	} // end else
#endif

#if ( (RASTERIZER_MODE==RASTERIZER_FAST) || (RASTERIZER_MODE==RASTERIZER_FASTEST) )
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;
	} // end if top is off screen

	if (y3 > m_clipRect.bottom)
		y3 = m_clipRect.bottom;

	// make sure top left fill convention is observed
	iy1 = ceil(y1);
	iy3 = ceil(y3)-1;
#endif 

	//Write_Error("\nTri-Bottom: xs=%f, xe=%f, y1=%f, y3=%f, iy1=%d, iy3=%d", xs,xe,y1,y3,iy1,iy3);

	// compute starting address in video memory
	dest_addr = dest_buffer + iy1*lpitch_2;

	// test if x clipping is needed
	if (x1 >= m_clipRect.left && x1 <= m_clipRect.right &&
		x2 >= m_clipRect.left && x2 <= m_clipRect.right &&
		x3 >= m_clipRect.left && x3 <= m_clipRect.right)
	{
		// draw the triangle
		for (loop_y = iy1; loop_y <= iy3; loop_y++, dest_addr+=lpitch_2)
		{
			//Write_Error("\nxs=%f, xe=%f", xs,xe);
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(xs),color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;
		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (loop_y = iy1; loop_y <= iy3; loop_y++,dest_addr+=lpitch_2)
		{
			// do x clip
			left  = xs;
			right = xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}

			//Write_Error("\nleft=%f, right=%f", left,right);
			// draw the line
			Mem_Set_WORD(dest_addr+(unsigned int)(left),color,(unsigned int)((int)right-(int)left+1));

		} // end for

	} // end else x clipping needed
	
	return 0;
} // end Draw_Bottom_Tri2_16

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Trig_2D2_16(	float x1,float y1,
									float x2,float y2,
									float x3,float y3,
									int color)
{
	// this function draws a triangle on the destination buffer
	// it decomposes all triangles into a pair of flat top, flat bottom

	float temp_x, // used for sorting
		temp_y,
		new_x;

#ifdef DEBUG_ON
	// track rendering stats
	debug_polys_rendered_per_frame++;
#endif


	// test for h lines and v lines
	if ((FCMP(x1,x2) && FCMP(x2,x3))  ||  (FCMP(y1,y2) && FCMP(y2,y3)))
		return 0;

	// sort p1,p2,p3 in ascending y order
	if (y2 < y1)
	{
		SWAP(x1,x2,temp_x);
		SWAP(y1,y2,temp_y);
	} // end if

	// now we know that p1 and p2 are in order
	if (y3 < y1)
	{
		SWAP(x1,x3,temp_x);
		SWAP(y1,y3,temp_y);
	} // end if

	// finally test y3 against y2
	if (y3 < y2)
	{
		SWAP(x2,x3,temp_x);
		SWAP(y2,y3,temp_y);
	} // end if

	// do trivial rejection tests for clipping
	if ( y3 < m_clipRect.top || y1 > m_clipRect.bottom ||
		(x1 < m_clipRect.left && x2 < m_clipRect.left && x3 < m_clipRect.left) ||
		(x1 > m_clipRect.right && x2 > m_clipRect.right && x3 > m_clipRect.right) )
		return 0;

	// test if top of triangle is flat
	if (FCMP(y1,y2))
	{
		Draw_Top_Tri2_16(x1,y1,x2,y2,x3,y3,color);
	} // end if
	else
		if (FCMP(y2,y3))
		{
			Draw_Bottom_Tri2_16(x1,y1,x2,y2,x3,y3,color);
		} // end if bottom is flat
		else
		{
			// general triangle that's needs to be broken up along long edge
			new_x = x1 + (y2-y1)*(x3-x1)/(y3-y1);

			// draw each sub-triangle
			Draw_Bottom_Tri2_16(x1,y1,new_x,y2,x2,y2,color);
			Draw_Top_Tri2_16(x2,y2,new_x,y2,x3,y3,color);
		} // end else

	return 0;
} // end Draw_Trig_2D2_16

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Top_Tri2(	float x1,float y1, 
								float x2,float y2, 
								float x3,float y3,
								int color)
{
	// this function draws a triangle that has a flat top

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height,      // the height of the triangle
		temp_x,      // used during sorting as temps
		temp_y,
		right,       // used by clipping
		left;

	int iy1,iy3,loop_y; // integers for y loops

	// destination address of next scanline
	UCHAR  *dest_addr = NULL;

	// test order of x1 and x2
	if (x2 < x1)
	{
		SWAP(x1,x2,temp_x);
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x3-x1)/height;
	dx_right = (x3-x2)/height;

	// set starting points
	xs = x1;
	xe = x2;

#if (RASTERIZER_MODE==RASTERIZER_ACCURATE)
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;

		// make sure top left fill convention is observed
		iy1 = y1;
	} // end if top is off screen
	else
	{
		// make sure top left fill convention is observed
		iy1 = ceil(y1);

		// bump xs and xe appropriately
		xs = xs+dx_left*(iy1-y1);
		xe = xe+dx_right*(iy1-y1);
	} // end else

	if (y3 > m_clipRect.bottom)
	{
		// clip y
		y3 = m_clipRect.bottom;

		// make sure top left fill convention is observed
		iy3 = y3-1;
	} // end if
	else
	{
		// make sure top left fill convention is observed
		iy3 = ceil(y3)-1;
	} // end else
#endif

#if ( (RASTERIZER_MODE==RASTERIZER_FAST) || (RASTERIZER_MODE==RASTERIZER_FASTEST) )
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;
	} // end if top is off screen

	if (y3 > m_clipRect.bottom)
		y3 = m_clipRect.bottom;

	// make sure top left fill convention is observed
	iy1 = ceil(y1);
	iy3 = ceil(y3)-1;
#endif 

	// compute starting address in video memory
	dest_addr = m_pBuffer + iy1 * m_lpitch;

	// test if x clipping is needed
	if (x1>=m_clipRect.left && x1<=m_clipRect.right &&
		x2>=m_clipRect.left && x2<=m_clipRect.right &&
		x3>=m_clipRect.left && x3<=m_clipRect.right)
	{
		// draw the triangle
		for (loop_y=iy1; loop_y<=iy3; loop_y++,dest_addr+=m_lpitch)
		{
			// draw the line
			memset((UCHAR *)dest_addr+(unsigned int)xs, color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;
		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle
		for (temp_y=iy1; temp_y<=iy3; temp_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = xs;
			right = xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}
			// draw the line
			memset((UCHAR  *)dest_addr+(unsigned int)left, color,(unsigned int)((int)right-(int)left+1));
		} // end for

	} // end else x clipping needed

	return 0;
} // end Draw_Top_Tri2

/////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Bottom_Tri2(	float x1,float y1, 
									float x2,float y2, 
									float x3,float y3,
									int color)
{
	// this function draws a triangle that has a flat bottom

	float dx_right,    // the dx/dy ratio of the right edge of line
		dx_left,     // the dx/dy ratio of the left edge of line
		xs,xe,       // the starting and ending points of the edges
		height,      // the height of the triangle
		temp_x,      // used during sorting as temps
		temp_y,
		right,       // used by clipping
		left;

	int iy1,iy3,loop_y; // integers for y loops

	// destination address of next scanline
	UCHAR  *dest_addr;

	// test order of x1 and x2
	if (x3 < x2)
	{
		SWAP(x2,x3,temp_x);
	} // end if swap

	// compute delta's
	height = y3-y1;

	dx_left  = (x2-x1)/height;
	dx_right = (x3-x1)/height;

	// set starting points
	xs = x1;
	xe = x1; 

#if (RASTERIZER_MODE==RASTERIZER_ACCURATE)
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;

		// make sure top left fill convention is observed
		iy1 = y1;
	} // end if top is off screen
	else
	{
		// make sure top left fill convention is observed
		iy1 = ceil(y1);

		// bump xs and xe appropriately
		xs = xs+dx_left*(iy1-y1);
		xe = xe+dx_right*(iy1-y1);
	} // end else

	if (y3 > m_clipRect.bottom)
	{
		// clip y
		y3 = m_clipRect.bottom;

		// make sure top left fill convention is observed
		iy3 = y3-1;
	} // end if
	else
	{
		// make sure top left fill convention is observed
		iy3 = ceil(y3)-1;
	} // end else
#endif

#if ( (RASTERIZER_MODE==RASTERIZER_FAST) || (RASTERIZER_MODE==RASTERIZER_FASTEST) )
	// perform y clipping
	if (y1 < m_clipRect.top)
	{
		// compute new xs and ys
		xs = xs+dx_left*(-y1+m_clipRect.top);
		xe = xe+dx_right*(-y1+m_clipRect.top);

		// reset y1
		y1 = m_clipRect.top;
	} // end if top is off screen

	if (y3 > m_clipRect.bottom)
		y3 = m_clipRect.bottom;

	// make sure top left fill convention is observed
	iy1 = ceil(y1);
	iy3 = ceil(y3)-1;
#endif 

	// compute starting address in video memory
	dest_addr = m_pBuffer + iy1 * m_lpitch;

	// test if x clipping is needed
	if (x1 >= m_clipRect.left && x1 <= m_clipRect.right &&
		x2 >= m_clipRect.left && x2 <= m_clipRect.right &&
		x3 >= m_clipRect.left && x3 <= m_clipRect.right)
	{
		// draw the triangle
		for (loop_y = iy1; loop_y <= iy3; loop_y++,dest_addr+=m_lpitch)
		{
			// fill the line
			memset((UCHAR *)dest_addr+(unsigned int)xs, color,(unsigned int)((int)xe-(int)xs+1));

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;
		} // end for

	} // end if no x clipping needed
	else
	{
		// clip x axis with slower version

		// draw the triangle

		for (loop_y = iy1; loop_y <= iy3; loop_y++,dest_addr+=m_lpitch)
		{
			// do x clip
			left  = xs;
			right = xe;

			// adjust starting point and ending point
			xs+=dx_left;
			xe+=dx_right;

			// clip line
			if (left < m_clipRect.left)
			{
				left = m_clipRect.left;

				if (right < m_clipRect.left)
					continue;
			}

			if (right > m_clipRect.right)
			{
				right = m_clipRect.right;

				if (left > m_clipRect.right)
					continue;
			}
			// fill the line
			memset((UCHAR *)dest_addr+(unsigned int)left, color,(unsigned int)((int)right-(int)left+1));
		} // end for

	} // end else x clipping needed

	return 0;

} // end Draw_Bottom_Tri2

///////////////////////////////////////////////////////////////////////////////

int CGraphTrig::Draw_Trig_2D2(	float x1,float y1,
								float x2,float y2,
								float x3,float y3,
								int color )
{
	// this function draws a triangle on the destination buffer
	// it decomposes all triangles into a pair of flat top, flat bottom

	float	temp_x, // used for sorting
			temp_y,
			new_x;

#ifdef DEBUG_ON
	// track rendering stats
	debug_polys_rendered_per_frame++;
#endif


	// test for h lines and v lines
	if ((FCMP(x1,x2) && FCMP(x2,x3))  ||  (FCMP(y1,y2) && FCMP(y2,y3)))
		return 0;

	// sort p1,p2,p3 in ascending y order
	if (y2 < y1)
	{
		SWAP(x1,x2,temp_x);
		SWAP(y1,y2,temp_y);
	} // end if

	// now we know that p1 and p2 are in order
	if (y3 < y1)
	{
		SWAP(x1,x3,temp_x);
		SWAP(y1,y3,temp_y);
	} // end if

	// finally test y3 against y2
	if (y3 < y2)
	{
		SWAP(x2,x3,temp_x);
		SWAP(y2,y3,temp_y);
	} // end if

	// do trivial rejection tests for clipping
	if ( y3 < m_clipRect.top || y1 > m_clipRect.bottom ||
		(x1 < m_clipRect.left && x2 < m_clipRect.left && x3<m_clipRect.left) ||
		(x1 > m_clipRect.right && x2 > m_clipRect.right && x3>m_clipRect.right) )
		return 0;

	// test if top of triangle is flat
	if (FCMP(y1,y2))
	{
		Draw_Top_Tri2(x1,y1,x2,y2,x3,y3,color);
	} // end if
	else
		if (FCMP(y2,y3))
		{
			Draw_Bottom_Tri2(x1,y1,x2,y2,x3,y3,color);
		} // end if bottom is flat
		else
		{
			// general triangle that's needs to be broken up along long edge
			new_x = x1 + (y2-y1)*(x3-x1)/(y3-y1);

			// draw each sub-triangle
			Draw_Bottom_Tri2(x1,y1,new_x,y2,x2,y2,color);
			Draw_Top_Tri2(x2,y2,new_x,y2,x3,y3,color);

		} // end else


	return 0;

} // end Draw_Trig_2D2